Skip to content

Add spatial predicates API with fixed-scale support#56

Merged
NailxSharipov merged 8 commits intoiShape-Rust:mainfrom
bretttully:gh-50/spatial-predicates
Feb 7, 2026
Merged

Add spatial predicates API with fixed-scale support#56
NailxSharipov merged 8 commits intoiShape-Rust:mainfrom
bretttully:gh-50/spatial-predicates

Conversation

@bretttully
Copy link
Contributor

@bretttully bretttully commented Feb 1, 2026

Summary

  • Add FloatPredicateOverlay and FloatRelate trait for efficient spatial relationship testing
  • Add FixedScaleFloatRelate trait for fixed-scale precision predicates
  • Support predicates: intersects, disjoint, interiors_intersect, touches, within, covers
  • Early-exit optimization returns as soon as predicate can be determined
  • Extract unified SweepRunner struct that owns scan structures and runs sweeps with any handler
    • Eliminates duplicated scan management code between GraphBuilder and PredicateOverlay
    • Enables FillHandler trait for early-exit during sweep (used by predicates)

Test plan

  • All existing tests pass (cargo test -p i_overlay)
  • New predicate tests added in relate.rs and scale.rs
  • Doctests pass for new examples
  • cargo clippy and cargo fmt pass

@bretttully
Copy link
Contributor Author

Implements #50

@bretttully bretttully changed the title gh-50 - Add spatial predicates API with fixed-scale support Add spatial predicates API with fixed-scale support Feb 1, 2026
@codecov
Copy link

codecov bot commented Feb 1, 2026

Codecov Report

❌ Patch coverage is 94.73684% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
iOverlay/src/build/sweep.rs 90.00% 6 Missing ⚠️
iOverlay/src/core/predicate.rs 93.58% 5 Missing ⚠️
iOverlay/src/core/relate.rs 95.12% 2 Missing ⚠️
iOverlay/src/build/builder.rs 88.88% 1 Missing ⚠️
iOverlay/src/float/scale.rs 97.50% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Add FloatPredicateOverlay and FloatRelate trait for efficient spatial
relationship testing (intersects, touches, within, covers, disjoint).

Features:
- PredicateOverlay for integer-space predicate evaluation with early-exit
- FloatPredicateOverlay wrapper handling float-to-int conversion
- FloatRelate trait for ergonomic predicate methods on ShapeResource types
- FixedScaleFloatRelate trait for fixed-scale precision predicates
- with_adapter() and with_adapter_custom() constructors for custom adapters
- with_subj_and_clip_fixed_scale() for fixed-scale precision
@bretttully bretttully force-pushed the gh-50/spatial-predicates branch from b15a9a8 to b145cbb Compare February 2, 2026 00:24
bretttully and others added 2 commits February 2, 2026 10:49
…cates

Add tests for:
- FixedScaleFloatRelate: disjoint_with_fixed_scale, covers_with_fixed_scale
- FloatOverlay: with_subj_and_clip_fixed_scale_custom
- FloatPredicateOverlay: with_adapter, with_adapter_custom, with_subj_and_clip_custom, clear
- Error handling for invalid scale values across all fixed-scale methods
Add add_path_iter, add_contour, add_contours, add_shape, and add_shapes
methods to match Overlay's public API. This enables external crates to
use PredicateOverlay directly without accessing internal fields.

Refactored FloatPredicateOverlay to use the new public methods.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@NailxSharipov
Copy link
Member

Thanks for the pr.
It's quite large, so I've only had a quick look. Overall it looks mostly fine, but I haven't viewed it precisely yet and it will take time.

Copy link
Member

@NailxSharipov NailxSharipov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work.
I like how this is implemented.


#[inline(always)]
fn handle(&mut self, index: usize, fill: SegmentFill) -> ControlFlow<()> {
self.fills[index] = fill;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to use unsafe here.
The compiler doesn't see this as safe and won't optimize it. But the algorithm guarantees that it return an index less than the vector's length.

};
use core::ops::ControlFlow;

/// Handler that checks if subject and clip shapes intersect (share any point).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can not handle point <-> point case with this logic.

   ┌──┐
   │S │
┌──●──┘
│C │
└──┘

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch -- added here: 0b94dcb

… indexing

- Use unsafe unchecked indexing in StoreFillsHandler for performance
- Add point coincidence detection for single-vertex touches between shapes
- Refactor TouchesHandler to return (boundary_contact, interior_overlap) tuple
  with early-exit on interior overlap
- Make evaluate() generic over output type to support tuple returns
- Add tests for point-to-point and segment endpoint touch scenarios
@bretttully bretttully force-pushed the gh-50/spatial-predicates branch from 0b94dcb to 430a64e Compare February 3, 2026 00:37
… access

Change FillHandler trait to receive segment reference, enabling handlers
to access segment data during sweep for point coincidence detection.

Key changes:
- FillHandler<C> now passes &Segment<C> to handle() method
- Add PointCoincidenceChecker helper struct with optimizations:
  - Uses IntPoint (8 bytes) vs (i32,i32,bool,bool) (16 bytes) = 2x memory reduction
  - Collects into separate subj/clip Vecs, sorts with sort_by_two_keys
  - Dedup removes ~50% duplicate endpoints from adjacent segments
  - Binary search from shorter array into longer for O(min(S,C) * log(max(S,C)))
  - Skips interior segments (SUBJ_BOTH/CLIP_BOTH fill) that can't contribute
- IntersectsHandler and TouchesHandler now collect points during sweep
- Remove standalone has_point_coincidence function from relate.rs
- Add integration tests for doughnut+diamond hole boundary scenarios
@bretttully
Copy link
Contributor Author

@NailxSharipov added those optimisations you suggested here: c4e52dc

…on detection

Add a new spatial predicate that returns true only when shapes touch by
vertex coincidence without any edge overlap. This fills the gap between
touches() (which includes edge contact) and interiors_intersect().

- Add PointIntersectsHandler with early exit on interior/boundary overlap
- Add point_intersects() to PredicateOverlay and FloatPredicateOverlay
- Add point_intersects() to FloatRelate trait
…r array

- Sort and dedup only the shorter array (binary search target)
- Iterate the longer array doing binary searches into the shorter
- Reduces complexity from O(n log n + m log m) to O(n log n + m log n)
- Fix test to expect false for shared segments (line, not point intersection)
@NailxSharipov NailxSharipov merged commit e35f194 into iShape-Rust:main Feb 7, 2026
2 checks passed
@bretttully bretttully deleted the gh-50/spatial-predicates branch February 7, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants